package com.xbongbong.saas.help.workflow;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.BusinessTypeErrorCodeEnum;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.StockFlowBillEntity;
import com.xbongbong.saas.domain.entity.StockFlowBillUpdateEntity;
import com.xbongbong.saas.domain.entity.StockSurplusRecordEntity;
import com.xbongbong.saas.enums.InstockTypeEnum;
import com.xbongbong.saas.enums.OutstockTypeEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.StockTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractOutstockEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.PurchaseInstockEnum;
import com.xbongbong.saas.enums.business.RefundInstockEnum;
import com.xbongbong.saas.enums.business.ReturnedPurchaseOutstockEnum;
import com.xbongbong.saas.enums.business.WorkOrderOutstockEnum;
import com.xbongbong.saas.model.StockFlowBillModel;
import com.xbongbong.saas.model.StockSurplusRecordModel;
import com.xbongbong.sys.model.CompanyConfigModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 *
 * @author 魏荣杰
 *
 */
@Component
@Slf4j
public class StockFlowBillHelp {

    private static final Logger LOG = LoggerFactory.getLogger(StockFlowBillHelp.class);
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private StockFlowBillModel stockFlowBillModel;
    @Resource
    private StockSurplusRecordModel stockSurplusRecordModel;


    /**
     * Description:
     * @param corpid
	 * @param businessType
	 * @param data 新数据
	 * @param paasFormDataEntity 新数据
     * @return void
     * @throws
     * @author 魏荣杰
     * @date 2021/7/23 11:21
     * @since
     */
    public void caculateStockModification4Save(String corpid, Integer businessType, JSONObject data, PaasFormDataEntity paasFormDataEntity) throws XbbException {
        if (Objects.equals(XbbRefTypeEnum.INSTOCK.getCode(), businessType)) {
            Integer instockType = data.getInteger(InstockEnum.TYPE.getAttr());
            businessType = InstockTypeEnum.getByCode(instockType).getStockBusinessType();
        } else if (Objects.equals(XbbRefTypeEnum.OUTSTOCK.getCode(), businessType)) {
            Integer outstockType = data.getInteger(OutstockEnum.TYPE.getAttr());
            businessType = OutstockTypeEnum.getByCode(outstockType).getStockBusinessType();
        }
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
        boolean isInstock = Arrays.asList(XbbRefTypeEnum.INSTOCK,XbbRefTypeEnum.PURCHASE_INSTOCK,XbbRefTypeEnum.REFUND_INSTOCK,XbbRefTypeEnum.OTHER_INSTOCK,XbbRefTypeEnum.PRODUCTION_INSTOCK,XbbRefTypeEnum.RETURNED_MATERIEL_INSTOCK).contains(xbbRefTypeEnum);
        boolean isOutstock = Arrays.asList(XbbRefTypeEnum.OUTSTOCK,XbbRefTypeEnum.CONTRACT_OUTSTOCK,XbbRefTypeEnum.RETURNED_PURCHASE_OUTSTOCK,XbbRefTypeEnum.OTHER_OUTSTOCK,XbbRefTypeEnum.PRODUCTION_MATERIEL_OUTSTOCK,XbbRefTypeEnum.WORK_ORDER_OUTSTOCK).contains(xbbRefTypeEnum);
        boolean isOriStock = Objects.equals(businessType, XbbRefTypeEnum.ORI_STOCK.getCode());
        boolean isCostAdjust = Objects.equals(businessType, XbbRefTypeEnum.COST_ADJUST.getCode());
//        PaasFormDataEntity paasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        Long refId = paasFormDataEntity.getId();
        Long dateTime = data.getLong(OutstockEnum.TIME.getAttr());
        if (Objects.isNull(dateTime)) {
            dateTime = DateTimeUtil.getTodayInt();
        }
        try {
            Integer stockType;
            if (isInstock) {
                stockType = StockTypeEnum.IN_STOCK.getCode();
            } else if (isOutstock) {
                stockType = StockTypeEnum.OUT_STOCK.getCode();
            } else if (isOriStock) {
                stockType = StockTypeEnum.ORI_STOCK.getCode();
            } else if (isCostAdjust) {
                stockType = StockTypeEnum.ADJUST_STOCK.getCode();
            } else {
                throw new XbbException(BusinessTypeErrorCodeEnum.API_ERROR_361001);
            }
            // 查询最新的结存
            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put("corpid", corpid);
            params.put("del", 0);
            params.put("lessEqualThanDate", dateTime);
            params.put("orderByStr", " date desc, id desc");
            JSONArray productArray = data.getJSONArray(OutstockEnum.PRODUCT.getAttr());
            Set<String> warehouseKeyList = new HashSet<>();
            // 合并相同产品和相同仓库的数量，以便于批量更新
            Map<String, StockFlowBillUpdateEntity> productWarehouseMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (int i = 0; i < productArray.size(); i++) {
                StringBuilder stringBuilder = new StringBuilder();
                JSONObject productObject = productArray.getJSONObject(i);
                Long pageProductId = productObject.getLong(SelectProductEnum.PRODUCT.getAttr());
                Long warehouseId;
                Object warehouseObj = productObject.get(SelectProductEnum.WAREHOUSE.getAttr());
                if (warehouseObj instanceof Long || warehouseObj instanceof Integer || NumberUtils.isNumber(String.valueOf(warehouseObj))) {
                    warehouseId = productObject.getLong(SelectProductEnum.WAREHOUSE.getAttr());
                } else {
                    JSONArray warehouseArray = productObject.getJSONArray(SelectProductEnum.WAREHOUSE.getAttr());
                    JSONObject warehosueObj = warehouseArray.getJSONObject(0);
                    warehouseId = warehosueObj.getLong("id");
                }
                Double num = productObject.getDouble(SelectProductEnum.NUM.getAttr());
                if (isOutstock) {
                    num = Arith.sub(0D, num);
                }
                Double cost = productObject.getDouble(SelectProductEnum.COST.getAttr());
                String warehouseKey = stringBuilder.append(pageProductId).append("_").append(warehouseId).toString();
                warehouseKeyList.add(warehouseKey);
                StockFlowBillUpdateEntity stockFlowBillUpdateEntity =  productWarehouseMap.get(warehouseKey);
                if (Objects.isNull(stockFlowBillUpdateEntity)) {
                    stockFlowBillUpdateEntity = new StockFlowBillUpdateEntity(warehouseKey, num, Arith.mul(num, cost), dateTime, DateTimeUtil.getInt(), refId);
                } else {
                    stockFlowBillUpdateEntity.updateStock(num, cost);
                }
                productWarehouseMap.put(warehouseKey, stockFlowBillUpdateEntity);
            }
            params.put("warehouseKeyIn", warehouseKeyList);
            List<StockFlowBillEntity> stockFlowBillList = stockFlowBillModel.getLatestFlowBillByWarehouseKey(params);
            // 当前插入数据的前一条流水记录
            Map<String, StockFlowBillEntity> startTimeMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            stockFlowBillList.forEach(item -> {
                startTimeMap.put(item.getWarehouseKey(), item);
                warehouseKeyList.remove(item.getWarehouseKey());
            });
            // 当前插入数据的前一条流水记录
            Map<String, StockSurplusRecordEntity> startTimeRecordMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            if (!warehouseKeyList.isEmpty()) {
                List<StockSurplusRecordEntity> stockSurplusRecordList = stockSurplusRecordModel.getLatestFlowBillByWarehouseKey(params);
                stockSurplusRecordList.forEach(item -> {
                    startTimeRecordMap.put(item.getWarehouseKey(), item);
                });
            }
            List<StockFlowBillEntity> addList = new ArrayList<>();
            String serialNo = paasFormDataEntity.getSerialNo();
            Long businessId = null;
            String businessName = null;
            if (Objects.equals(businessType, XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode())) {
                businessId = data.getLong(ContractOutstockEnum.LINK_CUSTOMER.getAttr());
                businessName = data.getString(ContractOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
            } else if (Objects.equals(businessType, XbbRefTypeEnum.REFUND_INSTOCK.getCode())) {
                businessId = data.getLong(RefundInstockEnum.CUSTOMER_ID.getAttr());
                businessName = data.getString(RefundInstockEnum.CUSTOMER_ID_LINKED_TEXT.getAttr());
            } else if (Objects.equals(businessType, XbbRefTypeEnum.PURCHASE_INSTOCK.getCode())) {
                businessId = data.getLong(PurchaseInstockEnum.SUPPLIER_ID.getAttr());
                businessName = data.getString(PurchaseInstockEnum.SUPPLIER_ID_LINKED_TEXT.getAttr());
            } else if (Objects.equals(businessType, XbbRefTypeEnum.RETURNED_PURCHASE_OUTSTOCK.getCode())) {
                businessId = data.getLong(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER.getAttr());
                businessName = data.getString(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER_HIDE.getAttr());
            } else if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER_OUTSTOCK.getCode())) {
                businessId = data.getLong(WorkOrderOutstockEnum.LINK_CUSTOMER.getAttr());
                businessName = data.getString(WorkOrderOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
            }
            for (int i = 0; i < productArray.size(); i++) {
                StringBuilder stringBuilder = new StringBuilder();
                JSONObject productObject = productArray.getJSONObject(i);
                Long pageProductId = productObject.getLong(SelectProductEnum.PRODUCT.getAttr());
                Long warehouseId;
                Object warehouseObj = productObject.get(SelectProductEnum.WAREHOUSE.getAttr());
                if (warehouseObj instanceof Long || warehouseObj instanceof Integer || NumberUtils.isNumber(String.valueOf(warehouseObj))) {
                    warehouseId = productObject.getLong(SelectProductEnum.WAREHOUSE.getAttr());
                } else {
                    JSONArray warehouseArray = productObject.getJSONArray(SelectProductEnum.WAREHOUSE.getAttr());
                    JSONObject warehosueObj = warehouseArray.getJSONObject(0);
                    warehouseId = warehosueObj.getLong("id");
                }
                Long productSubId = productObject.getLong(BusinessConstant.PRODUCT_SUB_ID);
                Double num = productObject.getDouble(SelectProductEnum.NUM.getAttr());
                Double cost = productObject.getDouble(SelectProductEnum.COST.getAttr());
                String memo = productObject.getString(SelectProductEnum.MEMO.getAttr());
                String key = stringBuilder.append(pageProductId).append("_").append(warehouseId).toString();
                if (!startTimeMap.containsKey(key)) {
                    if (startTimeRecordMap.containsKey(key)) {
                        Double remainingStock = num;
                        double remainingCost = Arith.mul(num, cost);
                        StockSurplusRecordEntity stockSurplusRecordEntity = startTimeRecordMap.get(key);
                        if (isInstock || isOriStock) {
                            remainingStock = Arith.add(stockSurplusRecordEntity.getRemainingStock(), remainingStock);
                            remainingCost = Arith.add(stockSurplusRecordEntity.getRemainingCost(), remainingCost);
                        } else if (isCostAdjust) {
                            cost = productObject.getDouble(BusinessConstant.ADJUST_AMOUNT);
                            remainingStock = num;
                            remainingCost = cost;
                        } else {
                            remainingStock = Arith.sub(stockSurplusRecordEntity.getRemainingStock(), remainingStock);
                            remainingCost = Arith.sub(stockSurplusRecordEntity.getRemainingCost(), remainingCost);
                        }
                        StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, warehouseId, refId, productSubId, stockType, pageProductId, serialNo, businessId, businessName, num, cost, remainingStock, remainingCost, dateTime, businessType, key, memo);
                        startTimeMap.put(key, stockFlowBillEntity);
                        addList.add(stockFlowBillEntity);
                    } else {
                        Double remainingStock = num;
                        double remainingCost = Arith.mul(num, cost);
                        if (isOutstock) {
                            remainingStock = Arith.sub(0D, remainingStock);
                            remainingCost = Arith.sub(0D, remainingCost);
                        } else if (isCostAdjust) {
                            cost = productObject.getDouble(BusinessConstant.ADJUST_AMOUNT);
                            remainingStock = num;
                            remainingCost = cost;
                        }
                        StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, warehouseId, refId, productSubId, stockType, pageProductId, serialNo, businessId, businessName, num, cost, remainingStock, remainingCost, dateTime, businessType, key, memo);
                        startTimeMap.put(key, stockFlowBillEntity);
                        addList.add(stockFlowBillEntity);
                    }
                } else {
                    Double remainingStock = num;
                    double remainingCost = Arith.mul(num, cost);
                    StockFlowBillEntity oldStockFlowBillEntity = startTimeMap.get(key);
                    if (isInstock || isOriStock) {
                        remainingStock = Arith.add(oldStockFlowBillEntity.getRemainingStock(), remainingStock);
                        remainingCost = Arith.add(oldStockFlowBillEntity.getRemainingCost(), remainingCost);
                    } else if (isCostAdjust) {
                        Double oldCost = oldStockFlowBillEntity.getRemainingCost();
                        Double oldStock = oldStockFlowBillEntity.getRemainingStock();
                        Double adjustCost = productObject.getDouble(BusinessConstant.ADJUST_AMOUNT);
                        cost = productObject.getDouble(BusinessConstant.ADJUST_AMOUNT);
                        remainingStock = oldStock;
                        remainingCost = Arith.add(oldCost, adjustCost);
                    } else {
                        remainingStock = Arith.sub(oldStockFlowBillEntity.getRemainingStock(), remainingStock);
                        remainingCost = Arith.sub(oldStockFlowBillEntity.getRemainingCost(), remainingCost);
                    }
                    StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, warehouseId, refId, productSubId, stockType, pageProductId, serialNo, businessId, businessName, num, cost, remainingStock, remainingCost, dateTime, businessType, key, memo);
                    startTimeMap.put(key, stockFlowBillEntity);
                    addList.add(stockFlowBillEntity);
                }
            }
            // 先插入
            if (!addList.isEmpty()) {
                stockFlowBillModel.insertBatch(addList);
            }
            // 更新数据
            stockFlowBillModel.updateGreaterThanTime(productWarehouseMap.values(), corpid, productWarehouseMap.keySet());
        } catch (XbbException e) {
            log.warn(String.format(StringConstant.STOCK_FLOW_BILL_MSG, corpid, businessType, refId),e);
        } catch (Exception e) {
            log.warn(String.format(StringConstant.STOCK_FLOW_BILL_MSG, corpid, businessType, refId),e);
        }
    }

    public void caculateStockModification4Delete(List<Long> deleteList, List<StockFlowBillUpdateEntity> stockFlowBillUpdateList, Integer businessType, String corpid) throws XbbException {
        try {
            List<Integer> businessTypeIn;
            if (Objects.equals(businessType, XbbRefTypeEnum.INSTOCK.getCode())) {
                businessTypeIn = Arrays.asList(XbbRefTypeEnum.PURCHASE_INSTOCK.getCode(),XbbRefTypeEnum.REFUND_INSTOCK.getCode(),XbbRefTypeEnum.OTHER_INSTOCK.getCode(),XbbRefTypeEnum.PRODUCTION_INSTOCK.getCode(),XbbRefTypeEnum.RETURNED_MATERIEL_INSTOCK.getCode());
            } else if (Objects.equals(businessType, XbbRefTypeEnum.OUTSTOCK.getCode())){
                businessTypeIn = Arrays.asList(XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode(),XbbRefTypeEnum.RETURNED_PURCHASE_OUTSTOCK.getCode(),XbbRefTypeEnum.OTHER_OUTSTOCK.getCode(),XbbRefTypeEnum.PRODUCTION_MATERIEL_OUTSTOCK.getCode(),XbbRefTypeEnum.WORK_ORDER_OUTSTOCK.getCode());
            } else if (Objects.equals(businessType, XbbRefTypeEnum.COST_ADJUST.getCode())){
                businessTypeIn = Collections.singletonList(XbbRefTypeEnum.COST_ADJUST.getCode());
            } else {
                businessTypeIn = Collections.singletonList(XbbRefTypeEnum.ORI_STOCK.getCode());

            }
            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put("corpid", corpid);
            params.put("del", 0);
            params.put("refIdIn", deleteList);
            params.put("businessTypeIn", businessTypeIn);
            List<StockFlowBillEntity> delStockFlowBillList = stockFlowBillModel.findEntitys(params);
            List<Long> hasStockFlow = new ArrayList<>();
            Map<String, List<Long>> warehouseKeyDateMap = new HashMap<>(delStockFlowBillList.size());
            delStockFlowBillList.forEach(item -> {
                String warehouseKeyDate = item.getWarehouseKey() + "_" + item.getDate();
                List<Long> idList = warehouseKeyDateMap.getOrDefault(warehouseKeyDate, new ArrayList<>());
                idList.add(item.getId());
                warehouseKeyDateMap.put(warehouseKeyDate, idList);
                hasStockFlow.add(item.getRefId());
            });
            warehouseKeyDateMap.values().forEach(item -> item.sort(Long::compareTo));
            stockFlowBillModel.deleteByRefIdIn(corpid, deleteList, businessTypeIn);
            // 有可能删除了没有流水记录的单据，说明是上线前的数据，这种情况应该是更新当前warehouseKey下的所有流水记录
            Map<String, StockFlowBillUpdateEntity> haveNoStockBillMap = new HashMap<>();
            Iterator<StockFlowBillUpdateEntity> iterator = stockFlowBillUpdateList.iterator();
            while (iterator.hasNext()) {
                StockFlowBillUpdateEntity stockFlowBillUpdateEntity = iterator.next();
                if (!hasStockFlow.contains(stockFlowBillUpdateEntity.getRefId())) {
                    if (haveNoStockBillMap.containsKey(stockFlowBillUpdateEntity.getWarehouseKey())) {
                        StockFlowBillUpdateEntity stockFlowBillUpdate = haveNoStockBillMap.get(stockFlowBillUpdateEntity.getWarehouseKey());
                        stockFlowBillUpdate.setCost(Arith.add(stockFlowBillUpdate.getCost(), stockFlowBillUpdateEntity.getCost()));
                        stockFlowBillUpdate.setStock(Arith.add(stockFlowBillUpdate.getStock(), stockFlowBillUpdateEntity.getStock()));
                    } else {
                        haveNoStockBillMap.put(stockFlowBillUpdateEntity.getWarehouseKey(), stockFlowBillUpdateEntity);
                    }
                    iterator.remove();
                }
            }
            if (!haveNoStockBillMap.isEmpty()) {
                stockFlowBillModel.updateBatch(haveNoStockBillMap.values(), corpid, haveNoStockBillMap.keySet());
            }
            // 剩余有流水记录的数据删除 产品按时间升序排序
            List<StockFlowBillUpdateEntity> sortedUpdateList = stockFlowBillUpdateList.stream().sorted(Comparator.comparing(StockFlowBillUpdateEntity::getStartTime)).collect(Collectors.toList());
            Map<String, List<StockFlowBillUpdateEntity>> warehouseKeySortedMap = new HashMap<>(sortedUpdateList.size());
            sortedUpdateList.forEach(item -> {
                String warehouseKeyDate = item.getWarehouseKey() + "_" + item.getStartTime();
                Long startId = warehouseKeyDateMap.get(warehouseKeyDate).get(0);
                item.setStartId(startId);
                warehouseKeyDateMap.get(warehouseKeyDate).remove(0);
                List<StockFlowBillUpdateEntity> updateList = warehouseKeySortedMap.getOrDefault(item.getWarehouseKey(), new ArrayList<>());
                if (!updateList.isEmpty()) {
                    StockFlowBillUpdateEntity lastStockFlowBillUpdateEntity = updateList.get(updateList.size() - 1);
                    lastStockFlowBillUpdateEntity.setEndTime(item.getStartTime());
                    lastStockFlowBillUpdateEntity.setEndId(startId);
                    item.setStock(Arith.add(lastStockFlowBillUpdateEntity.getStock(), item.getStock()));
                    item.setCost(Arith.add(lastStockFlowBillUpdateEntity.getCost(), item.getCost()));
                }
                updateList.add(item);
                warehouseKeySortedMap.put(item.getWarehouseKey(), updateList);
            });
            if (!warehouseKeySortedMap.isEmpty()) {
                stockFlowBillModel.updateGreaterThanTime(sortedUpdateList, corpid, warehouseKeySortedMap.keySet());
            }
        } catch (Exception e) {
            String refId = JSON.toJSONString(deleteList);
            LOG.warn(String.format(StringConstant.STOCK_FLOW_BILL_MSG, corpid, businessType, refId),e);
        }
    }
}
